package cxm.mmp.aud;

import android.media.AudioFormat;
import cxm.lib.util.MyLog;

public abstract class XAudioBase implements Runnable {
	private Thread thread = null;
	private boolean needContinue = false;
	protected IRecordingSource recording;

	protected byte[] buffer = null;
	
	// params for recording and playing
	protected int freq = 8000;
	protected int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
	protected int channel = AudioFormat.CHANNEL_IN_DEFAULT;
	
	protected abstract int onBeforeRun();
	protected abstract int onAfterRun();
	protected abstract int onRunOnce();

	public XAudioBase() {
		// new buffer for sending and receiving data
		this.buffer = new byte[1024];
	}
	
	// start record
	public void start() {
		if (null != this.thread)
			throw new IllegalStateException("Already start");
		
		// set thread flag
		this.needContinue = true;

		// staret thread for retrieve audio info
		this.thread = new Thread(this);
		this.thread.start();
	}
	
	// stop record
	public void stop() {
		if (null == this.thread) {
			MyLog.Log(MyLog.Level.Warning, "Already stopped");
			return;
		}
		
		this.needContinue = false;
		try {
			MyLog.Log(MyLog.Level.Debug, "Before join recording  thread");
			this.thread.join();
			MyLog.Log(MyLog.Level.Debug, "After join recording thread");
		} catch (Exception e) {
			MyLog.Log(MyLog.Level.Debug, "Cannot join thread: " + e);
		}
		
		this.thread = null;
	}

	@Override
	public void run() {
		MyLog.Log(MyLog.Level.Debug, "Beginning at run");

		int res = 0;
		do {
			// fire start notify
			res = this.onBeforeRun();
			if (0 != res) {
				MyLog.Log(MyLog.Level.Error, "Cannot start thread: " + res);
				break;
			}
			// fire recording source notify
			if (null != this.recording) {
				res = this.recording.onRecordStart();
				if (0 != res) {
					MyLog.Log(MyLog.Level.Error, "Cannot start recording data: " + res);
					// fire recording error
					this.recording.onRecordError(IRecordingSource.RecordError.StartError, res);
					break;
				}
			}

			// fire runonce notify
			while (this.needContinue) {
				res = this.onRunOnce();
				if (0 != res) {
					MyLog.Log(MyLog.Level.Error, "Run once failed: " + res);
					// fire error notify
					if (null != this.recording) {
						res = this.recording.onRecordError(
								IRecordingSource.RecordError.DataError, res);
						// abandon remained data process if necessary
						if (0 == res) {
							MyLog.Log(MyLog.Level.Warning, "Abandon data process");
							break;
						}
					}
				}
			}

			// fire notify
			if (null != this.recording) {
				res = this.recording.onRecordStop();
				if (0 != res) {
					MyLog.Log(MyLog.Level.Error, "Cannot stop recording data");
					// fire recording error
					this.recording.onRecordError(IRecordingSource.RecordError.StopError, res);
					break;
				}
			}
		} while(false);

		// fire stop nofity
		res = this.onAfterRun();
		if (0 != res)
			MyLog.Log(MyLog.Level.Error, "After thread stop failed: " + res);
		// mark state
		this.needContinue = false;

		MyLog.Log(MyLog.Level.Debug, "Endding at run");
	}

	public boolean isStart() {
		return null != this.thread;
	}
	
	public void setRecording(IRecordingSource recording){
		if (null != this.recording)
			throw new IllegalStateException("Already set recording");

		this.recording = recording;
	}
	
	public void setBufferLength(int bufferLength) {
		if (null != this.thread)
			throw new IllegalStateException("Already started");
		
		this.buffer = new byte[bufferLength];
	}
}
